package com.smartalarmclock.Model;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;

import com.smartalarmclock.Model.Exceptions.DataEntityDoesNotExistException;
import com.smartalarmclock.Model.Exceptions.InvalidSaveDataEntityOperationException;
import com.smartalarmclock.Model.Exceptions.PhoneContactDoesNotExistException;


import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds;
import android.util.Log;

final class SingleContact extends Contact implements ISingleContact {
	
	// Members
	
	protected static final String[] c_QueryAllColumnsContact = new String[] { c_ColumnNameId, c_ContactColumnNamePhoneContact };
	
	private String m_PhoneContactId;
	
	// Constructors
	
	/**
	 * Public Constructor for a new SingleContact
	 * @param phoneContactId the phone contact id as it is saved in the phone
	 */
	protected SingleContact(String phoneContactId) {
		super();
		m_PhoneContactId = phoneContactId;
	}
	
	/**
	 * Private Constructor for creation after querying an existing SingleContact
	 * @param id the id in the DB of the matching SingleContact
	 * @param phoneNumberId the phone contact id as it is saved in the phone
	 */
	private SingleContact(int id, String phoneContactId) {
		super(id);
		m_PhoneContactId = phoneContactId;
	}
	
	// ISingleContact implementation
	
	/**
	 * @return phone contact id
	 */
	public String GetPhoneContactId() {
		return m_PhoneContactId;
	}
	
	/**
	 * @return the contact's display name as saved in the phone's contact list
	 * @throws PhoneContactDoesNotExistException if the contact with the matching phone contact id is missing
	 * in the phone contacts database
	 */
	public String GetPhoneContactName(Context context) throws PhoneContactDoesNotExistException {
		Cursor cursor = context.getContentResolver().query(
			Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, m_PhoneContactId),
			new String[] { ContactsContract.Contacts.DISPLAY_NAME },
			null, null, null);
		
		try {
			if (!cursor.moveToNext()) {
				throw new PhoneContactDoesNotExistException(m_PhoneContactId);
			}
			
			return cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
		}
		finally {
			cursor.close();
		}
	}
	
	@Override
	public ArrayList<String> GetPhoneContactNumbers(Context context) throws PhoneContactDoesNotExistException  {
		// For Debug:
		ArrayList<String> phones = new ArrayList<String>();
		phones.add("15555215556");
		return phones;
		/*
		Log.v("SingleContact", "Trying to get phone numbers for " + m_PhoneContactId);
		ArrayList<String> phones = new ArrayList<String>();

		String [] PROJECTION = new String [] {  ContactsContract.Contacts.LOOKUP_KEY };

		Cursor cursor = context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, PROJECTION, null, null, null);

		for(cursor.moveToFirst(); cursor.moveToNext(); cursor.isAfterLast()) {
		     Log.d("SingleContact", "lookupKey for contact:  " + cursor.getString(1) + ", is: " + cursor.getString(0));
		     phones.add(cursor.getString(cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER)));
		}

	    cursor.close();
	    return(phones);
	    */
	}
	// Public Methods
	
	/**
	 * @param context the context of the request (the originating Activity)
	 * @param id the id of the SingleContact
	 * @return the SingleContact instance from the DB with the corresponding id
	 * @throws DataEntityDoesNotExistException if the SingleContact was not found
	 */
	static SingleContact GetSingleContactById(Context context, int id) throws DataEntityDoesNotExistException {
		return QuerySingleContact(
			context,
			c_ContactColumnNameType + "=" + ContactType.SingleContact.getValue() + " AND " +
			c_ColumnNameId + "=" + id);
	}
	
	/**
	 * @param context the context of the request (the originating Activity)
	 * @param id the id of the SingleContact
	 * @return the SingleContact instance from the DB with the corresponding id, null if doesn't exist
	 */
	static SingleContact TryGetSingleContactById(Context context, int id) {
		try {
			return GetSingleContactById(context, id);
		} catch (DataEntityDoesNotExistException e) {
			return null;
		}
	}
	
	/**
	 * @param context the context of the request (the originating Activity)
	 * @param phoneContactId the id of the contact as it is saved in the phone's contact list
	 * @return the SingleContact instance from the DB with the corresponding phone contact id
	 * @throws DataEntityDoesNotExistException if the SingleContact was not found
	 */
	static SingleContact GetSingleContactByPhoneContactId(Context context, String phoneContactId) throws DataEntityDoesNotExistException {
		return QuerySingleContact(
			context,
			c_ContactColumnNameType + "=" + ContactType.SingleContact.getValue() + " AND " +
			c_ContactColumnNamePhoneContact + " LIKE " + "\"" + phoneContactId + "\"");
	}
	
	/**
	 * @param context the context of the request (the originating Activity)
	 * @param phoneContactId the id of the contact as it is saved in the phone's contact list
	 * @return the SingleContact instance from the DB with the corresponding phone contact id, null if doesn't exist
	 */
	static SingleContact TryGetSingleContactByPhoneContactId(Context context, String phoneContactId) {
		try {
			return GetSingleContactByPhoneContactId(context, phoneContactId);
		} catch (DataEntityDoesNotExistException e) {
			return null;
		}
	}
	
	/**
	 * @param context the context of the request (the originating Activity)
	 * @param phoneNumber phone number of the contact
	 * @return the SingleContact instance from the DB with the corresponding phone number
	 * @throws PhoneContactDoesNotExistException if a contact with the given phone number does not exist in the phone's contacts database.
	 * @throws DataEntityDoesNotExistException if the SingleContact was not found.
	 */
	static SingleContact GetSingleContactByPhoneNumber(Context context, String phoneNumber) throws PhoneContactDoesNotExistException, DataEntityDoesNotExistException {
		Cursor cursor = context.getContentResolver().query(
			ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
			new String[] { ContactsContract.Contacts.LOOKUP_KEY },
			ContactsContract.CommonDataKinds.Phone.NUMBER + " LIKE " + phoneNumber,
			null, null);
		
		try {
			if (!cursor.moveToNext()) {
				throw new PhoneContactDoesNotExistException(phoneNumber);
			}
			
			String phoneContactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
			return GetSingleContactByPhoneContactId(context, phoneContactId);
		}
		finally {
			cursor.close();
		}
	}
	
	/**
	 * @param context the context of the request (the originating Activity)
	 * @param phoneNumber phone number of the contact
	 * @return the SingleContact instance from the DB with the corresponding phone number, null if doesn't exist
	 */
	static SingleContact TryGetSingleContactByPhoneNumber(Context context, String phoneNumber) {
		try {
			return GetSingleContactByPhoneNumber(context, phoneNumber);
		} catch (Exception e) {
			return null;
		}
	}
	
	/**
	 * Queries the DB for all single contacts
	 * @param context the context of the request (the originating Activity)
	 * @return all SingleContacts in the DB
	 */
	static Set<ISingleContact> GetAllSingleContacts(Context context) {
		SQLiteDatabase db = OpenDBForReading(context);
		Cursor cursor = db.query(
			ContactTableName,
			c_QueryAllColumnsContact,
			c_ContactColumnNameType + "=" + ContactType.SingleContact.getValue(),
			null, null, null, null);
		
		HashSet<ISingleContact> contacts = new HashSet<ISingleContact>(cursor.getCount());
		while (cursor.moveToNext()) {
			contacts.add(CreateSingleContactFromCursor(cursor));
		}
		
		cursor.close();
		db.close();
		return contacts;
	}
	
	// Private Methods
	
	/**
	 * Creates a new SingleContact instance from the values of the record in the cursor
	 * @param cursor the DB query cursor placed on a SingleContact valid record.
	 * @return the corresponding SingleContact instance
	 */
	private static SingleContact CreateSingleContactFromCursor(Cursor cursor) {
		int id = cursor.getInt(cursor.getColumnIndex(c_ColumnNameId));
		String phoneContactId = cursor.getString(cursor.getColumnIndex(c_ContactColumnNamePhoneContact));
		return new SingleContact(id, phoneContactId);
	}
	
	/**
	 * Queries the DB for a single record with the corresponding where clause 
	 * @param context the context of the request (the originating Activity)
	 * @param whereClause the where clause for the query
	 * @return the SingleContact instance with the matching record's values.
	 * @throws DataEntityDoesNotExistException if the SingleContact is not found in the database.
	 */
	private static SingleContact QuerySingleContact(Context context, String whereClause) throws DataEntityDoesNotExistException {
		SQLiteDatabase db = OpenDBForReading(context);
		Cursor cursor = db.query(ContactTableName, c_QueryAllColumnsContact, whereClause, null, null, null, null);
		
		try {
			if (!cursor.moveToNext()) {
				throw new DataEntityDoesNotExistException(SingleContact.class, whereClause);
			}
			
			return CreateSingleContactFromCursor(cursor);
		}
		finally {
			cursor.close();
			db.close();
		}
	}
	
	// DataEntity implementation
	
	@Override
	protected Hashtable<String, ContentValues> GetRecordValues() {
		ContentValues values = new ContentValues();
		
		values.put(c_ContactColumnNameType, ContactType.SingleContact.getValue());
		values.put(c_ContactColumnNamePhoneContact, m_PhoneContactId);
		
		Hashtable<String, ContentValues> valuesTable = new Hashtable<String, ContentValues>();
		valuesTable.put(ContactTableName, values);
		return valuesTable;
	}
	
	// IDataEntity implementation
	
	@Override
	public void Refresh(Context context) throws DataEntityDoesNotExistException {
		SingleContact tempContact = GetSingleContactById(context, GetId());
		m_PhoneContactId = tempContact.GetPhoneContactId();
	}
	
	/**
	 * Saves the instance in the database.
	 * If a SingleContact with the matching phone contact id appears in the DB, nothing is done. 
	 */
	@Override
	public void Save(Context context) throws InvalidSaveDataEntityOperationException {
		if (IsNewInstance() && TryGetSingleContactByPhoneContactId(context, m_PhoneContactId) != null) {
			// A SingleContact with the phone contact id already exists so we should not add another.
			// Since no other fields should be updated we don't need to update the record.
			return;
		}
		
		super.Save(context);
	}
	
	// IContact implementation
	
	/**
	 * @return whether the contact is a single contact or not.
	 */
	@Override
	public boolean IsSingleContact() {
		return true;
	}

	/**
	 * @return whether the contact is a group or not.
	 */
	@Override
	public boolean IsGroup() {
		return false;
	}
	
	@Override
	public String GetName(Context context) throws PhoneContactDoesNotExistException {
		return GetPhoneContactName(context);
	}
	
	// Object implementation
	
	@Override
	public String toString() {
		return String.format("SingleContact %s", GetPhoneContactId());
	}
	
	@Override
	public int hashCode() {
		return toString().hashCode();
	}
}
